package sdmaker.ast;

import java.util.ArrayList;
import java.util.List;

import javax.rmi.CORBA.Util;

import fsa.State;
import fsa.Transition;

import sdmaker.parser.SetStateData;
import sdmaker.parser.ParseResult;
import sdmaker.parser.SDMakerGenerator;
import sdmaker.parser.SDMakerParser;
import sdmaker.parser.StateData;
import sdmaker.parser.Utils;
import traces.Statement;

public class ASTcall implements AST {
	private String sender;
	private String receiver;
	private String method_name;
	private List<AST> nested_call;



	/* ************************ *
	 * ***** CONSTRUCTORS ***** *
	 * ************************ */

	public ASTcall(Statement st){
		sender=st.getSender().getName();
		receiver= st.getReceiver().getName();
		method_name= st.getMethod().getName();
		nested_call=null;
	}

	public ASTcall(Statement st, List<AST>nested_call){
		this(st);
		this.nested_call= nested_call;
	}



	/* ************************ *
	 * ******* TESTERS ******** *
	 * ************************ */

	public static boolean leadToSimpleCall(
			StateData n, SetStateData seenStates, 
			SetStateData forbiddenStates){
		return findSimpleCall(n, seenStates, forbiddenStates)!=null;
	}

	public static boolean leadToNestedCall(
			StateData n, SetStateData seenStates, 
			SetStateData forbiddenStates){
		return (findNestedCall(n, seenStates, forbiddenStates)!=null);
	}



	/* ************************ *
	 * ******** OTHERS ******** *
	 * ************************ */

	private static Transition findSimpleCall(
			StateData n, SetStateData seenStates, 
			SetStateData forbiddenStates){
		Transition leadToSimpleCall= null;
		StateData nextState= null;
		Statement closureStatement= null;
		Statement nextStatement= null;

		for(int i= 0; i < n.getNbSuccesseur(); i++){

			nextState= n.getNextStateData(i);

			if(forbiddenStates.contains(nextState)==false&&
					n.isAlreadyParsedTransition(n.getSuccesseur(i))==false&&
					seenStates.contains(nextState)==false &&
					nextState.getSuccesseurs().size()==1){

				closureStatement= 
						((Statement)n
								.getSuccesseur(i)
								.getTrigger()).getClosingStatement();
				nextStatement= 
						(Statement)nextState
						.getSuccesseurs()
						.get(0).getTrigger();

				if(Utils.sameStatement(closureStatement, nextStatement)){
					leadToSimpleCall= n.getSuccesseur(i);
					break;
				}
			}
		}

		return leadToSimpleCall;
	}

	private static Transition findNestedCall(
			StateData n, SetStateData seenStates, 
			SetStateData forbiddenStates){
		Transition leadToNestedCall= null;

		for(int i= 0; i < n.getNbSuccesseur(); i++){

			if(n.isAlreadyParsedTransition(n.getSuccesseur(i))==false &&
					seenStates.contains(n.getNextStateData(i))==false &&
					leadToSimpleCall(n, seenStates, forbiddenStates)==false){
				leadToNestedCall= n.getSuccesseur(i);
			}

			Statement statement= 
					(Statement)n
					.getSuccesseurs()
					.get(0)
					.getTrigger();
			if(Utils.isClosingStatement(statement)){
				leadToNestedCall= null;
			}
			else{
				break;
			}
		}

		return leadToNestedCall;
	}

	public static void parseSimpleCall(
			SDMakerGenerator generator, StateData n, 
			SetStateData seenStates, SetStateData forbiddenStates){

		Transition founded= findSimpleCall(n, seenStates, forbiddenStates);
		StateData startingState= n;
		StateData nextOfStarting= StateData.getStateData(founded.getTarget());
		StateData finishingState= nextOfStarting.getNextStateData(0);
		AST resultingAST= new ASTcall(
				(Statement)founded
				.getTrigger());
		Transition lastTransition= finishingState.getPredecesseurs().get(0);

		seenStates.add(startingState);
		seenStates.add(nextOfStarting);
		seenStates.add(finishingState);

		n.addParsedTransition(n.getSuccesseur(0));
		nextOfStarting.addParsedTransition(nextOfStarting.getSuccesseur(0));

		generator.add(new ParseResult(
				startingState, finishingState, 
				resultingAST, lastTransition, 
				generator.getLastParsed(), null,
				seenStates));
	}

	public static void parseNestedCall(SDMakerGenerator generator, StateData n, SetStateData seenStates, SetStateData forbiddenStates){//, boolean addtoForbiddenForRecursive){
		StateData nextStateToParse= 
				StateData.getStateData(findNestedCall(
								n, seenStates, forbiddenStates).getTarget());
		List<AST> nested_ast= new ArrayList<AST>();
		String closingStatement= 
				((Statement)n
						.getSuccesseurs()
						.get(0)
						.getTrigger())
						.getClosingStatement()
						.toString();
		String currentStatement=null;
		Transition lastTransition= null;
		ParseResult lastParsed= null;

		do{
			SDMakerParser.parseState(
					generator, nextStateToParse, 
					seenStates, forbiddenStates);

			nested_ast.add(generator.getLastParsed().getResultingAST());
			lastParsed= generator.getLastParsed();
			generator.remove(lastParsed);
			nextStateToParse= StateData.getStateData(
					lastParsed.getLastTransition().getTarget());

			currentStatement= 
					((Statement)lastParsed
							.getLastTransition()
							.getTarget()
							.getSuccesseurs()
							.get(0)
							.getTrigger())
							.toString();

			lastTransition= lastParsed
					.getLastTransition()
					.getTarget()
					.getSuccesseurs()
					.get(0);

		}while(closingStatement.compareTo(currentStatement)!=0);

		StateData startingState= n;
		StateData penultimate= lastParsed.getFinishingState();
		StateData finishingState= penultimate.getNextStateData(0);

		AST nested_call= 
				new ASTcall(
						(Statement)n
						.getSuccesseurs()
						.get(0)
						.getTrigger(), nested_ast);

		seenStates.add(startingState);
		seenStates.add(penultimate);
		seenStates.add(finishingState);

		n.addParsedTransition(n.getSuccesseur(0));
		penultimate.addParsedTransition(penultimate.getSuccesseur(0));

		generator.add(new ParseResult(
				startingState, 
				finishingState, 
				nested_call, 
				lastTransition,
				generator.getLastParsed(), null,
				seenStates));
	}

	public String toString(){
		String result="";

		result+="Call "+sender+"->"+receiver+"."+method_name+"()";

		if(nested_call==null){
			result+=";";
		}
		else{
			result+="{";
			for(AST ast : nested_call){
				result+="\n\t"+ast;
			}
			result+="\n}";
		}

		return result;
	}
}
